#include "esp_camera.h"
#include <WiFi.h>

#include <PubSubClient.h>
#include <ArduinoJson.h>

#define CAMERA_MODEL_AI_THINKER
#include "camera_pins.h"

const char* ssid = "ESP32_STM32";
const char* password = "12345678";

void startCameraServer();
static bool gpio_isr_installed = false;  // 新增中断服务状态标记



// 巴法云配置
const char* mqttServer = "bemfa.com";
const int mqttPort = 9501;
const char* clientID = "bc5f98d096129decbe9a348b5da34f88";      // 替换为你的客户端ID
const char* username = "";      // 替换为你的用户名
const char* mqttPassword = "";  // 替换为你的密码

// 主题数据结构
struct TopicData {
  const char* camelName;  // 驼峰式主题名
  const char* underlineName; // 下划线格式名
  int value;              // 当前值
  bool isNumber;          // 是否为数值类型
};

// 主题初始化配置
TopicData topics[] = {
  {"LMZ057TargetTemp",   "target_temp",   0, true},  // 新增目标温度
  {"LMZ057TargetHumi",   "target_humi",   0, true},  // 新增目标湿度
  {"LMZ057Temp",  "dht11_temp",  0, true},
  {"LMZ057Humi",    "dht11_humi",    0, true},
  //{"Buzz",         "buzz",          0, false},
  {"LMZ057Servo",        "servo",         0, false},
//  {"AutoHumi",     "auto_humi",     0, false},
//  {"PtcHot",       "ptc_hot",       0, false},
  {"LMZ057Fan",          "fan",           0, false}
};

WiFiClient espClient;
PubSubClient client(espClient);

// 串口缓冲区
String serialBuffer = "";

// 自定义大写字母判断函数
bool isUpperCase(char c) {
  return (c >= 'A' && c <= 'Z');
}



void setup() {
  Serial.begin(115200);
  Serial.setDebugOutput(true);
  Serial.println();


  client.setServer(mqttServer, mqttPort);
  client.setCallback(mqttCallback);

  // 修复GPIO中断服务重复安装问题
  if (!gpio_isr_installed) {
    gpio_install_isr_service(0);
    gpio_isr_installed = true;
  }

  // 优化硬件复位时序
  #if defined(PWDN_GPIO_NUM)
    pinMode(PWDN_GPIO_NUM, OUTPUT);
    digitalWrite(PWDN_GPIO_NUM, HIGH);  // 先拉高确保完全复位
    delay(100);
    digitalWrite(PWDN_GPIO_NUM, LOW);
    delay(500);  // 延长复位时间
  #endif

  camera_config_t config;
  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer = LEDC_TIMER_0;
  config.pin_d0 = Y2_GPIO_NUM;
  config.pin_d1 = Y3_GPIO_NUM;
  config.pin_d2 = Y4_GPIO_NUM;
  config.pin_d3 = Y5_GPIO_NUM;
  config.pin_d4 = Y6_GPIO_NUM;
  config.pin_d5 = Y7_GPIO_NUM;
  config.pin_d6 = Y8_GPIO_NUM;
  config.pin_d7 = Y9_GPIO_NUM;
  config.pin_xclk = XCLK_GPIO_NUM;
  config.pin_pclk = PCLK_GPIO_NUM;
  config.pin_vsync = VSYNC_GPIO_NUM;
  config.pin_href = HREF_GPIO_NUM;
  config.pin_sccb_sda = SIOD_GPIO_NUM;
  config.pin_sccb_scl = SIOC_GPIO_NUM;
  config.pin_pwdn = PWDN_GPIO_NUM;
  config.pin_reset = RESET_GPIO_NUM;
  config.xclk_freq_hz = 20000000;
  config.frame_size = FRAMESIZE_HVGA;
  config.pixel_format = PIXFORMAT_RGB565;
  config.grab_mode = CAMERA_GRAB_LATEST;
  config.fb_location = CAMERA_FB_IN_PSRAM;
  config.jpeg_quality = 12;
  config.fb_count = 3;

  // 增加电源稳定延迟
  delay(1000);

  // 修复重复初始化问题
  esp_err_t err = ESP_FAIL;
  for(int i=0; i<5; i++) {  // 增强重试机制
    err = esp_camera_init(&config);
    if (err == ESP_OK) break;
    delay(1000 * (i+1));  // 指数退避
  }

  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);
    return;  // 初始化失败直接退出
  }

  // 添加sensor指针检查
  sensor_t *s = esp_camera_sensor_get();
  if(s == NULL) {
    Serial.println("Camera sensor detect failed");
    return;
  }

  if (s->id.PID == OV3660_PID) {
    s->set_vflip(s, 1);
    s->set_brightness(s, 1);
    s->set_saturation(s, -2);
  }

  WiFi.begin(ssid, password);
  WiFi.setSleep(false);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  
  startCameraServer();
  Serial.print("Camera Ready! Use 'http://");
  Serial.print(WiFi.localIP());
  Serial.println("' to connect");
}

void loop() {
  delay(10);

if (!client.connected()) {
    reconnect();
  }
  client.loop();

   handleSerialInput();

}


// MQTT重连
void reconnect() {
  while (!client.connected()) {
    if (client.connect(clientID, username, mqttPassword)) {
      // 订阅所有主题
      for (int i = 0; i < sizeof(topics)/sizeof(topics[0]); i++) {
        client.subscribe(topics[i].camelName);
      }
    } else {
      delay(5000);
    }
  }
}

// MQTT消息回调
void mqttCallback(char* topic, byte* payload, unsigned int length) {
  String message;
  for (int i = 0; i < length; i++) {
    message += (char)payload[i];
  }

  bool updated = false;
  for (int i = 0; i < sizeof(topics)/sizeof(topics[0]); i++) {
    if (strcmp(topic, topics[i].camelName) == 0) {
      int newValue = message.toInt();
      
      // 统一验证逻辑
      if (topics[i].isNumber) {  // 所有数值类型主题
        if (newValue >= 1 && newValue <= 100) {
          topics[i].value = newValue;
          updated = true;
        }
      } else {                   // 布尔类型主题
        if (newValue == 0 || newValue == 1) {
          topics[i].value = newValue;
          updated = true;
        }
      }
      break;
    }
  }

  if (updated) {
    sendAllTopicsToSerial();
  }
}

// 发送全量数据到串口
void sendAllTopicsToSerial() {
  String output = "";
  for (int i = 0; i < sizeof(topics)/sizeof(topics[0]); i++) {
    output += topics[i].underlineName;
    output += ":";
    output += topics[i].value;
    output += "/";
  }
  Serial.println(output);
}

// 处理串口输入
void handleSerialInput() {
  while (Serial.available()) {
    char c = Serial.read();
    if (c == '\n') {
      processSerialData();
      serialBuffer = "";
    } else {
      serialBuffer += c;
    }
  }
}
// 处理串口数据
void processSerialData() {
  int startIndex = 0;
  while (startIndex < serialBuffer.length()) {
    int endIndex = serialBuffer.indexOf('/', startIndex);
    if (endIndex == -1) endIndex = serialBuffer.length();
    
    String segment = serialBuffer.substring(startIndex, endIndex);
    processSegment(segment);
    
    startIndex = endIndex + 1;
  }
}

// 处理单个数据段
void processSegment(String segment) {
  int colonIndex = segment.indexOf(':');
  if (colonIndex == -1) return;
  
  String rawTopic = segment.substring(0, colonIndex);
  String valueStr = segment.substring(colonIndex + 1);
  
  String camelTopic = underlineToCamel(rawTopic);
  int topicIndex = getTopicIndex(camelTopic);
  if (topicIndex == -1) return;

  int value = valueStr.toInt();
  
  // 统一验证逻辑
  if (topics[topicIndex].isNumber) {  // 数值类型主题
    if (value >= 1 && value <= 100) {
      publishData(topics[topicIndex].camelName, String(value).c_str());
    }
  } else {                           // 布尔类型主题
    if (value == 0 || value == 1) {
      publishData(topics[topicIndex].camelName, String(value).c_str());
    }
  }
}

// MQTT数据发布
void publishData(const char* topic, const char* message) {
  client.publish(topic, message);
}

// 下划线转驼峰
String underlineToCamel(String input) {
  String output;
  bool capitalize = true;
  for (unsigned int i = 0; i < input.length(); i++) {
    if (input[i] == '_') {
      capitalize = true;
    } else {
      output += capitalize ? (char)toupper(input[i]) : (char)tolower(input[i]);
      capitalize = false;
    }
  }
  return output;
}

// 驼峰转下划线
String camelToUnderline(String input) {
  String output;
  for (unsigned int i = 0; i < input.length(); i++) {
    char c = input[i];
    if (isUpperCase(c) && i != 0) {
      output += '_';
    }
    output += (char)tolower(c);
  }
  return output;
}

// 获取主题索引
int getTopicIndex(String topic) {
  for (int i = 0; i < sizeof(topics)/sizeof(topics[0]); i++) {
    // 比较字符串内容而不是对象
    if (topic.equals(String(topics[i].camelName))) {
      return i;
    }
  }
  return -1;
}

